package me.spring.cloud.common.components.util.net;

import cn.hutool.system.SystemUtil;
import me.spring.cloud.common.components.util.lang.StringUtil;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import lombok.extern.slf4j.Slf4j;

/**
 * @author luffy
 * @description: ping工具类
 * @date 2019-06-26 15:14
 */
@Slf4j
public class PingIpUtils {

  private static final String SYSTEM_TYPE_WINDOWS = "Windows";
  private static final String SYSTEM_TYPE_MACOS = "Mac OS X";
  private static final int DEFAULT_TIMEOUT_MS = 500;

  private static Pattern ip_patteern = Pattern.compile("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}");


  /**
   * @param address ip或域名
   * @param pingTimes 次数(一次ping,对方返回的ping的结果的次数)
   * @param timeOut 超时时间 单位ms(ping不通,设置的此次ping结束时间)
   * @Return
   * @Description ping
   */
  public static boolean ping(String address, int pingTimes, int timeOut) {
    BufferedReader in = null;
    Runtime r = Runtime.getRuntime();
    String osName = SystemUtil.get(SystemUtil.OS_NAME);
    String pingCommand = constructPing(address, pingTimes, timeOut);
    try {
      in = execute(r, in, pingCommand);
      if (null == in) {
        return false;
      }
      int connectedCount = 0;
      String line = null;
      while ((line = in.readLine()) != null) {
        connectedCount += getCheckResult(line, osName);
      }
      log.debug("ping通设备IP的次数为:" + connectedCount);
      return connectedCount >= 2;
    } catch (Exception ex) {
      log.error(ex.getMessage(), ex);
      return false;
    } finally {
      try {
        assert in != null;
        in.close();
      } catch (IOException e) {
        log.error(e.getMessage(), e);
      }
    }
  }

  /**
   * @Return java.lang.String
   * @Author luffy
   * @Description ping域名，并获取解析的ip
   * @Date 2019-06-26 16:10
   * @Param [ipAddress, pingTimes, timeOut]
   **/
  public static String pingDomain(String ipAddress, int pingTimes, int timeOut) {
    BufferedReader in = null;
    Runtime r = Runtime.getRuntime();
    String pingCommand = constructPing(ipAddress, pingTimes, timeOut);
    try {
      in = execute(r, in, pingCommand);
      if (null == in) {
        return null;
      }
      Set<String> domainIP = new HashSet<>();
      String line = null;
      String tmpIP = null;
      while ((line = in.readLine()) != null) {
        tmpIP = getResultIp(line);
        if (!StringUtil.isEmpty(tmpIP)) {
          domainIP.add(tmpIP);
        }
      }
      log.debug("ping通设备IP的有：{}", domainIP);
      return StringUtil.join(domainIP);
    } catch (Exception ex) {
      log.error(ex.getMessage(), ex);
      return null;
    } finally {
      try {
        assert in != null;
        in.close();
      } catch (IOException e) {
        log.error(e.getMessage(), e);
      }
    }
  }


  /**
   * @Return java.lang.String
   * @Author luffy
   * @Description 组装ping 命令
   * @Date 2019-06-26 16:18
   * @Param [r, ipAddress, pingTimes, timeOut]
   **/
  private static String constructPing(String ipAddress, int pingTimes, int timeOut) {
    String pingCommand = null;
    String osName = SystemUtil.get(SystemUtil.OS_NAME);
    log.debug("项目所在系统是:" + osName);
    if (osName.contains(SYSTEM_TYPE_WINDOWS)) {
      //将要执行的ping命令,此命令是windows格式的命令
      pingCommand = "ping " + ipAddress + " -n " + pingTimes + " -w " + timeOut;
    } else if (osName.contains(SYSTEM_TYPE_MACOS)) {
      //将要执行的ping命令,此命令是Mac os格式的命令
      pingCommand = "ping " + " -c " + pingTimes + " -t " + timeOut + " " + ipAddress;
    } else {
      //将要执行的ping命令,此命令是Linux格式的命令
      //-c:次数,-w:超时时间(单位/ms)  ping -c 10 -w 0.5 192.168.120.206
      pingCommand = "ping " + " -c " + pingTimes + " -w " + timeOut + " " + ipAddress;
    }
    return pingCommand;
  }

  /**
   * @Return boolean
   * @Author luffy
   * @Description 执行sheel
   * @Date 2019-06-26 16:22
   * @Param [r, in, pingCommand]
   **/
  private static BufferedReader execute(Runtime r, BufferedReader in, String pingCommand) throws IOException {
    //执行命令并获取输出
    Process p = r.exec(pingCommand);
    if (p == null) {
      return null;
    }
    in = new BufferedReader(new InputStreamReader(p.getInputStream()));
    return in;
  }

  /**
   * @Return int
   * @Author luffy
   * @Description 若line含有=18 ms ttl=64字样,说明已经ping通,返回1,否則返回0.
   * @Date 2019-06-26 16:03
   * @Param [line, osName]
   **/
  private static int getCheckResult(String line, String osName) {
    if (osName.contains(SYSTEM_TYPE_WINDOWS)) {
      if (line.contains("TTL=")) {
        return 1;
      }
    } else {
      if (line.contains("ttl=")) {
        return 1;
      }
    }
    return 0;
  }

  /**
   * @Return String
   * @Author luffy
   * @Description 提取ping返回数据中的ip
   * @Date 2019-06-26 16:02
   * @Param [line]
   **/
  private static String getResultIp(String line) {
    return getIp(line);
  }

  /**
   * @Return java.lang.String
   * @Author luffy
   * @Description 字符串提取ip
   * @Date 2019-06-26 15:59
   * @Param [str]
   **/
  public static String getIp(String str) {
    Matcher matcher = ip_patteern.matcher(str);
    if (matcher.find()) {
      return matcher.group();
    }
    return null;
  }

  /**
   * @Return java.lang.String
   * @Author luffy
   * @Description 获取域名解析的ip
   * @Date 2019-06-26 16:10
   * @Param [domain]
   **/
  public String getDomainIp(String domain) {
    return pingDomain(domain, 1, DEFAULT_TIMEOUT_MS);
  }
}
